package cc.android.testapp.xp.hooker.textmodify;

import android.app.Activity;
import android.app.Application;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.text.SpannableString;
import android.text.style.BackgroundColorSpan;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

import org.json.JSONObject;

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;

import javax.annotation.Nullable;

import cc.android.testapp.cfg.CFG;
import cc.android.testapp.cfg.Config;
import cc.android.testapp.cfg.ConfigHelper;
import cc.android.testapp.util.CLog;
import cc.android.testapp.xp.HookBase;
import cc.android.testapp.xp.HookModule;
import cc.android.testapp.xp.hooker.textmodify.et.ETFactory;
import cc.android.testapp.xp.hooker.textmodify.et.ET_CustomTextView;
import cc.android.testapp.xp.hooker.textmodify.et.IET;
import de.robv.android.xposed.XC_MethodHook;
import de.robv.android.xposed.XposedHelpers;
import de.robv.android.xposed.callbacks.XC_LoadPackage;

@HookModule(name = "文本编辑")
public class HookEditText extends HookBase implements ConfigHelper.IConfig {

    public static String KEY_IGNORE = "IGNORE_HOOK";
    public static String KEY_HIGH_LIGHT = "FIELD_BOOLEAN_TEXTVIEW_HIGHLIGHT";
    public static String KEY_LOCK_DATA = "KEY_LOCK_DATA";
    public static String KEY_ORIGIN_DATA = "KEY_ORIGIN_DATA";

    public static Application mApp = null;
    @CFG
    public static boolean mEnableTextedit = false;
    @CFG
    private static boolean mEnableViewCheck = false;

    /**
     * 加载的自定义TextView配置
     */
    public static final ArrayList<ET_CustomTextView.CTView> mCTViews = new ArrayList<>();

    @Override
    public void onConfigChange() {
        ConfigHelper.IConfig.super.onConfigChange();

        mCTViews.clear();
        JSONObject tJson = Config.getConfig().optJSONObject("edittext");
        if (tJson != null) {
            Iterator<String> tKeys = tJson.keys();
            while (tKeys.hasNext()) {
                String sKey = tKeys.next();
                JSONObject tJsonSub = tJson.optJSONObject(sKey);
                if (tJsonSub != null) {
                    String tClazz = tJson.optString("class");
                    if (tClazz.isEmpty()) continue;
                    String tGetMethod = tJson.optString("get_method");
                    if (tGetMethod.isEmpty()) continue;
                    String tSetMethod = tJson.optString("set_method");
                    if (tSetMethod.isEmpty()) continue;
                    mCTViews.add(new ET_CustomTextView.CTView(tClazz, tGetMethod, tSetMethod));
                }

            }
        }
        if (mApp != null) ETFactory.init(mApp);
    }

    @Override
    public String getCfgNode() {
        return "";
    }

    @Override
    protected void hookToApp(XC_LoadPackage.LoadPackageParam pParam, Application pApp) throws Throwable {
        CLog.log("EditText model hook to->" + pParam.packageName);
        mApp = pApp;
        ETFactory.init(pApp);
        XposedHelpers.findAndHookMethod(View.class, "dispatchTouchEvent"
                , MotionEvent.class, new XC_MethodHook() {

                    @Override
                    protected void beforeHookedMethod(MethodHookParam pParam) {
                        View tView;
                        if (!HookEditText.canEdit(tView = (View) pParam.thisObject)) return;

                        if (mEnableTextedit) {
                            IET tWrap = ETFactory.getWarppedTextView(tView);
                            if (tWrap != null) {
                                if (((MotionEvent) pParam.args[0]).getAction() == MotionEvent.ACTION_DOWN
                                        && !tWrap.getText().toString().isEmpty()) {
                                    pParam.setResult(true);
                                    new ETDiaLog(tView.getContext(), tWrap.copy(), HookEditText.getListener(tView)).show();
                                }
                            } else if (tView instanceof ImageView) {
                                ImageView tIView = (ImageView) tView;
                                if (tIView.getDrawable() == null) return;
                                if (((MotionEvent) pParam.args[0]).getAction() == MotionEvent.ACTION_DOWN && tIView.getContext() instanceof Activity) {
                                    pParam.setResult(true);
                                    new EIDiaLog(tView.getContext(), tIView, HookEditText.getListener(tView)).show();
                                }
                            }
                        } else if (mEnableViewCheck && !ViewPropDialog.isShown()) {
                            MotionEvent tEvent = (MotionEvent) pParam.args[0];
                            if (tEvent.getAction() == MotionEvent.ACTION_DOWN) {
                                pParam.setResult(true);
                                new ViewPropDialog(tView.getContext(), pickPointViews(tEvent)).show();
                            } else pParam.setResult(true);
                        }
                    }

                });
        // 拦截锁定的ImageView
        XposedHelpers.findAndHookMethod(ImageView.class, "setImageDrawable", Drawable.class, new XC_MethodHook() {
            @Override
            protected void beforeHookedMethod(MethodHookParam pParam) {
                ImageView tView = (ImageView) pParam.thisObject;
                if (!mEnableTextedit || !HookEditText.canEdit(tView)) return;
                Object tLockData;
                if ((tLockData = HookEditText.getLockData(tView)) != null) {
                    Object tPImg = pParam.args[0];
                    if (tPImg instanceof BitmapDrawable) {
                        BitmapDrawable tBD = (BitmapDrawable) tPImg;
                        if (tBD.getBitmap() == tLockData) return;
                    }
                    pParam.setResult(null);
                    tView.setImageBitmap((Bitmap) tLockData);
                    HookEditText.setOriginData(tView, tPImg);
                }
            }
        });

        XposedHelpers.findAndHookMethod(Activity.class, "onResume", new XC_MethodHook() {
            @Override
            protected void afterHookedMethod(MethodHookParam param) {
                ViewPropDialog.mNowAct = new WeakReference<>((Activity) param.thisObject);
            }
        });
    }

    public static View.OnClickListener getListener(View pView) {
        Object tMethod = XposedHelpers.callMethod(pView, "getListenerInfo");
        return (View.OnClickListener) XposedHelpers.getObjectField(tMethod, "mOnClickListener");
    }

    public static void setHighLight(boolean pEnable) {
        XposedHelpers.setAdditionalStaticField(View.class, KEY_HIGH_LIGHT, pEnable);
    }

    public static boolean canHighLight(@Nullable View pView) {
        if (pView == null || KEY_IGNORE.equals(pView.getTag()))
            return false;

        return boolValue(XposedHelpers.getAdditionalStaticField(View.class, KEY_HIGH_LIGHT));
    }

    public static boolean canEdit(View pView) {
        return pView != null && (!KEY_IGNORE.equals(pView.getTag()));
    }

    public static <T extends View> T ignoreEdit(T pView) {
        if (pView != null) pView.setTag(KEY_IGNORE);
        return pView;
    }

    public static boolean isDataLocked(View pView) {
        return getLockData(pView) != null;
    }

    public static void setLockData(View pView, @Nullable Object pData) {
        if (!canEdit(pView)) return;
        if (pData == null) XposedHelpers.removeAdditionalInstanceField(pView, KEY_LOCK_DATA);
        else XposedHelpers.setAdditionalInstanceField(pView, KEY_LOCK_DATA, pData);
    }

    public static Object getLockData(View pView) {
        if (!canEdit(pView)) return null;
        return XposedHelpers.getAdditionalInstanceField(pView, KEY_LOCK_DATA);
    }

    public static void setOriginData(View pView, @Nullable Object pData) {
        if (!canEdit(pView)) return;
        if (pData == null) XposedHelpers.removeAdditionalInstanceField(pView, KEY_ORIGIN_DATA);
        else XposedHelpers.setAdditionalInstanceField(pView, KEY_ORIGIN_DATA, pData);
    }

    public static Object getOriginData(View pView) {
        if (!canEdit(pView)) return null;
        return XposedHelpers.getAdditionalInstanceField(pView, KEY_ORIGIN_DATA);
    }

    public static boolean boolValue(Object pV) {
        return (pV instanceof Boolean) && ((Boolean) pV);
    }

    public static List<View> pickPointViews(MotionEvent pEvent) {
        Activity tAct = ViewPropDialog.mNowAct.get();
        if (tAct == null) {
            CLog.log("Activity为空");
            return Collections.emptyList();
        }

        ArrayList<View> tViews = new ArrayList<>();
        ViewGroup tContent = (ViewGroup) tAct.getWindow().getDecorView();
        pickPointViews(pEvent.getRawX(), pEvent.getRawY(), tContent, tViews, tContent);
        if (tViews.isEmpty()) tViews.add(tContent);
        Collections.reverse(tViews);
        return tViews;
    }

    public static void pickPointViews(float pPointX, float pPointY, ViewGroup pGView, List<View> pViews, View pRoot) {
        int[] tPLoc = new int[2];
        pGView.getLocationOnScreen(tPLoc);
        int tLen = pGView.getChildCount();
        for (int i = 0; i < tLen; i++) {
            View tChild = pGView.getChildAt(i);
            if (tChild == null || !tChild.isShown()) continue;
            float tX = tChild.getX() + tPLoc[0];
            float tY = tChild.getY() + tPLoc[1];
            if (tX <= pPointX && pPointX <= tX + tChild.getWidth()
                    && tY <= pPointY && pPointY <= tY + tChild.getHeight()) {
                if (tChild instanceof ViewGroup) {
                    int tPer = pViews.size();
                    pickPointViews(pPointX, pPointY, (ViewGroup) tChild, pViews, pRoot);
                    if (tPer == pViews.size() && (tX != 0F || tY != 0F || tChild.getWidth() != pRoot.getWidth()
                            || tChild.getHeight() != pRoot.getHeight())) pViews.add(tChild);
                } else {
                    pViews.add(tChild);
                }
            }

        }
    }

}
